package org.wheel.base.proxy.factory;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.wheel.base.utils.ReflectionUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * @author klaus
 * @date 2021/4/2 华小勇 10:30
 * @description 动态代理生成类
 */
public class ProxyFactory<T> implements InvocationHandler, MethodInterceptor {

    protected T object;

    protected Class<T> interface_;

    protected BiFunction<Class<?>, Method, Function<Object[], Object>> methodProxy;

    /**
     * 只代理接口 不需要任何实现类
     *
     * @param interface_
     * @param methodProxy
     */
    public ProxyFactory(Class<T> interface_, BiFunction<Class<?>, Method, Function<Object[], Object>> methodProxy) {
        this.interface_ = interface_;
        this.methodProxy = methodProxy;
    }

    /**
     * cglib 代理对象(没有实现任何接口)
     *
     * @param object
     * @param methodProxy
     */
    public ProxyFactory(T object, BiFunction<Class<?>, Method, Function<Object[], Object>> methodProxy) {
        this.object = object;
        this.methodProxy = methodProxy;
    }

	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(object == null ? this : object, args);
        } else if (ReflectionUtils.isDefaultMethod(method)) {
            return ReflectionUtils.invokeDefaultMethod(proxy, method, args);
        }
        return this.getMethodProxy(method).apply(args);
    }

    private Function<Object[], Object> getMethodProxy(Method method) {
        return this.methodProxy.apply(interface_, method);
    }

    @SuppressWarnings("unchecked")
	public T newInstance() {
        return (T) Optional.ofNullable(object).map((o) -> Enhancer.create(o.getClass(), this))
                .orElseGet(() -> Proxy.newProxyInstance(interface_.getClassLoader(), new Class[]{interface_}, this));
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args,
                            org.springframework.cglib.proxy.MethodProxy methodProxy) throws Throwable {
        return this.invoke(proxy, method, args);
    }
}
